/**
 * Created by: Sam Olmsted
 * Created: 4-20-2012
 * Last edit: 4-24-2012
 * 
 * Opens a file and reads it's contents in order to convert it to a list of 
 * of boards
 **/
#inlcude "reader.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


/**
 * See header file
 **/
fileStuct* readFile(char* filename){ 
  /**
   * --1
   * [  countOfChars  := 0
   *    string        := size of one char
   *    file          := file handle 
   *    *container := allocated space big enough for a fileStruct]
   **/
  fileStruct* container = (fileStruct*) malloc(sizeof(fileStruct));
  int countOfChars      = 0;
  char* string          = (char *) malloc(sizeof(char));
  FILE* file            = fopen(filename, "r");

  /** 
   * --2
   * Check to see if the file was opened and if so return.  
   * [  if file did not open -> 
   *      container's status := ERROR 
   *      return 
   *    else -> I ]
   **/
  if(file == NULL){
    container->status = ERROR; 
    return container; 
  }
  
  /**
   * --3 
   * [  *buf := make space for the buffer ]
   **/
  struct stat *buf = (struct stat*) malloc(sizeof(struct stat));

  /**
   * --4
   * get the file status. Assume if it was opened then we can just get stat.
   * [  buf := information about the file, including size ]
   **/
  stat(file, buf);

  /**
   * --5
   * input to string 
   * [  string := string from the file  ]
   **/
  fread(string, buf->st_size, 1, file);

  /**
   * --6
   * [file := closed]
   **/
  fclose(file);

  /**
   * --7 
   * Set the values in the container.
   * [  container->value   := flag for a string is being passed back 
   *    container->string  := the read in string]
   */
  container->status = SUCCESS;
  container->string = string;

  /**
   * --7
   * return the container
   **/
  return container;
}

parseStruct* parseString(char* string){
  /**
   * --1
   * create handles and sets their values. 
   * [ *container     := space for a container
   *  **move          := space for pointers to chars
   *   *move          := space for chars
   *    numberOfMoves := 0
   *    end           := have more string to process]
   */
  parseStruct * container = (parseStruct*) malloc(sizeof(parseStruct));
  char ** moves           = (char**) malloc(sizeof(char**));
  char * move;
  int numberOfMoves       = 0;

  /**
   * --2
   * Loop over appendMove until they reach the end of the 
   * string. Each appendMove takes at most one char from 
   * the string. It is then easy to see that the end of the string 
   * will be reached. 
   * [  string         := at the end of the string 
   *    moves          := all the moves in the game
   *    numberOfMoves  := the number of moves ]
   **/
  while(*string != EOF){
    /**
     * --2 Body 
     * Add one more move.
     * [  string         := advanced by at least one char 
     *    moves         +:= add a move
     *    numberOfMoves +:= 1 ]
     **/

    /**
     * --2.1
     * The string is consumed until it finds the next move or EOF.
     * Append a move onto moves and increase numberOfMoves by 1.
     * [  if the end of the string is reached ->
     *      string         := EOF 
     *    else ->
     *      string         := advanced to after a move 
     *      moves         +:= the next move 
     *      numberOfMoves +:= 1]
     */
    appendMove(&string, moves, &numberOfMoves);
  }

  /**
   * --4 
   * Set the values in the container.
   * [  container->value := a flag for a list of moves is being passed back 
   *    container->moves := the moves of the game
   *    container->size  := the number of moves]
   */
  container->status  = SUCCESS;
  container->moves   = moves;
  container->size    = numberOfMoves;

  /**
   * --5
   * Return the container
   **/
  return container;
}

/**
 * Converts a array of moves into a list of positions.
 * [ positions := all the positions of the game ]
 **/
gameStruct* makeGame(char** moves, int size){
  /**
   * --1 
   * Set handles. 
   * [  game              := the first position 
   *    currentPosition   := a postion in the game 
   *    nextPosition      := a postion in the game 
   *    numberOfPositions := 0
   *    createdPositions  := 0  ]
   **/ 
  Position* game;
  Position* currentPosition; 
  Position* nextPosition; 
  int numberOfPositions = 0; 
  int createdPositions = 0;
  
  /**
   * --2 
   * An empty board.
   * [  currentBoard := an empty board 
   *    nextBoard    := handle for next boards ]
   **/
  board* board = createBoard(); 

  /**
   * --3 
   * Make the empty position.
   * [ game := a new position with board  ]
   **/
  game = createPosition(currentBoard);

  /**
   * --4 
   * create the first position, the board with the first move. 
   * [  board           := a copy of the current board 
   *    nextPosition    := the board with the first move
   *    currentPosition := the position with the board  ]
   **/
  board = copyBoard(board)
  makeNextBoard(moves[0], board, '#');
  nextPosition = createPosition(board);
  
  /**
   * --5 
   * Add the first move to game and increase createdPositions.
   * [  currentPosition   := game, the start, first move, pointing to the next 
   *    createdPositions +:= 1  ]
   **/
  currentPosition = game;
  addPosition(current, next); 
  createdPositions++;

  /**
   * --3
   * For the number of moves create boards. 
   * [  game            := the list of positions, which represents the game  
   *    currentPosition := the second to last position 
   *    nextPosition    := the last position  ]
   **/
  for(createdPositions; createdPositions<size; createdPositions++){
    /**
     * --3.1
     * Get ready to make the next move. 
     * [  currentPosition := next position 
     *    board           := a copy of the current board  ]
     **/
    currentPosition = next;
    board = copyBoard(board);

    /**
     * --3.2
     * Make the next position in the game. 
     * [  if it is black's move -> 
     *      board := add a black stone to the board with Go rules
     *    else ->
     *      board := add a white stone to the board with Go rules ]
     **/
    if( 0 == i%2){
      makePosition(moves[i], board, '#')
    }else{
      makePosition(moves[i], board, 'o')
    }

    /**
     * --4.1
     * create the next position. 
     * [  nextPosition    := the next board with
     *    currentPosition := the position with the board  ]
     **/
    makeNextBoard(moves[0], nextBoard, '#');
    nextPosition = createPosition(board);

    /**
     * --5.1
     * Add the first move to game and increase createdPositions.
     * [  current           := current + first move 
     *    createdPositions +:= 1  ]
     **/
    addPosition(current, next); 
    createdPositions++;
  }

  return game;
}


/******
 * Private Methods 
 * TODO declare these at top
 **/

/** 
 * Make move makes a move on the board
 * [ board := the board after the given move  ]
 **/
void makeNextBoard(char* move, board* board, char color){
  /**
   * --1 
   * handles
   **/
  int column, row;

  /**
   * --2 
   * convert column and row from alpha positions to numbers
   **/
  column = convert(move[0]);
  row    = convert(move[1]);

  /**
   * --3 
   * Make the next move on the board, from the last board  and the new
   * stone to be added. 
   * [ board   := next board  ]
   **/
  move(board, column, row, color);
}


/**
 * Convert the given alpha char to a number. Example 'a'->0 or 'E'->4.
 * [  num := the numerical equivalent of  alpha on the board  ]
 **/
int convert(char alpha){
  int num = 0; 

  /**
   * convert the alpha to its numerical board value.
   * [  if alpha is upper case ->
   *      num := alpha - 'Z'
   *    else -> 
   *      num := alpha - 'z'  ]
   **/
  if(alpha < 91){ //upper case
    num = ((int) alpha) - 41;
  } else { //lower case 
    num = ((int) alpha) - 97;
  }
  return num;   
}

/** 
 * Consume to the given string until the pattern is found or the 
 * end of the string is reached. 
 *
 * Example: 
 * +-----+---+---+-----+----+---+---+---+---+---+----+-----+
 * | ... | E | [ | ... |  B | [ | e | e | ] | ; | ...| EOF |
 * +-----+---+---+-----+----+---+---+---+---+---+----+-----+
 * 
 * want to find: B[ee]
 *
 * [  if string reaches EOF -> 
 *      return 0
 *    else ->
 *      string := advanced to next move ]
 **/
int consumeUntil(char** string, char color){
  /**
   * --1  
   * set values 
   **/
  int i=0;
  int end = 1;

  /**
   * --2 
   * loop until the color is found or the end of the string
   * [  if string reaches EOF -> 
   *      return 0
   *    else ->
   *      string := advanced to possible next move ]
   */
  for(i; end; i++){
    /**
     * --2.1
     * check to see if the color has been found and if so break 
     **/
    if((*string)[i] == color)
      end = 0;

    /**
     * --2.2
     * check to see if the end of the string has been found and if so 
     * return 0 to notify that there is no more string. 
     */
    if((*string)[i] == EOF)
      return 0;
  }

  /**
   * --3 
   * Check to see if the next char is '[', which makes either W[ or B[.
   * [  if string's next char is '[' -> 
   *      string := string moved to the start of next move
   *      return 1
   *    else ->
   *      return result from consumeUntil(string, color) ]
   **/ 
  if( (*string)[i+1] == '[' ){
    /**
     * --3.1
     * Consume the part of the string that has been checked
     **/
    (*string) = (*string)+i+1;
    return 1;
  }

  /**
   * --4 
   * Consume the part of the string that has been checked and continue
   * the search. 
   * [  string := string moved past the last possible match
   *    return result from consumeReturn(string, color) ] 
   **/
  (*string) = (*string)+i+1;
  return consumeUntil(string, color);
}

/**
 * Return the two alpha coordinates for the next move on the board. 
 * [  string := string moved past the last move
 *    move   := two coordinate chars ]
 **/
char * consumeReturn(char** string){
  /**
   * --1
   * set the values 
   * [  move := space for two chars  ]
   **/
  char* move = (char *) malloc(2*sizeof(char*));
  int num = 0;

  /**
   * --2
   * Collect the given move from the string
   * [  move := two coordinate chars ]
   **/
  while( **string != ']' ){
    /**
     * --2.1 body
     * keep looking for the two chars.
     * [  string := the next char between '[' and ']' for a move
     *    if string is ']' -> 
     *      move := the two coordinate chars]
     *    else ->
     *      if num is 0 -> 
     *        move[0] := string
     *      else -> 
     *        move[1] := string  ]
     *    
     **/
    if(num == 0){
      move[0] = **string;
    } else {
      move[1] = **string;
    }
    (*string)++;
  }
  
  return move;
}

/**
 * Append the next move onto moves.
 * [  string         := move the string up to next move or EOF 
 *    moves          := An array of strings of the moves   
 *    numberOfMoves +:= 1
 *    if the end of the string is reached ->
 *      return 0
 *    else ->
 *      return 1 ]
 **/
int appendMove(char** string, char ** moves, int * numberOfMoves){
  /**
   * --1
   * set values
   **/
  char  black = 'B';
  char  white = 'W';
  int numberOfMoves = 0; //used to figure out if it is white or blacks turn
  int end = 1;

  /**
   * --2
   * handles
   **/
  char * move;

  /**
   * --3
   * Find the next black or white move
   **/
  if( (numberOfMoves % 2) == 0 ){
    consumeUntil(string, black); 
  } else {
    consumeUntil(string, white); 
  }

  /**
   * --4 
   * check to see if the end of the string has been reached.
   * [ if the string is at the end -> 
   *    return 0
   *   else -> I  ]
   **/
  if (**string == EOF)
    return 0; 

  /**
   * --5
   * Find the next move 
   * [  move := the next move in the string  ]
   **/
  move = consumeReturn(string)

  /**
   * --6
   * Increase the space space for the next set of moves.
   * [  numberOfMoves +:= 1
   *    moves          := one more space for a move ] 
   **/
  numberOfMoves++; 
  moves = (char **) realloc(moves, (numberOfMoves)*sizeof(char**))

  /**
   * --7 
   * Add the new move to moves.
   * [  moves +:= the next move ]
   **/
  moves[numberOfMoves-1] = move;

  /**
   * --8 
   * return 1 to notify that there is still more string.
   **/
  return 1;
}
